package be.android.forap.smartcard;

import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.AESKey;
import javacard.security.CryptoException;
import javacard.security.Key;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.MessageDigest;
import javacard.security.RSAPrivateKey;
import javacard.security.RSAPublicKey;
import javacard.security.RandomData;
import javacardx.crypto.Cipher;

public class ForAp extends Applet{

	private final short KEY_PAIR_LENGTH = KeyBuilder.LENGTH_RSA_1024 ;
	private final byte KEY_PAIR_ALG = KeyPair.ALG_RSA;

	private final short SESSION_KEY_LENGTH = KeyBuilder.LENGTH_AES_128;
	private final byte SESSION_KEY_ALG = KeyBuilder.TYPE_AES;

	private final byte SYMMETRIC_ALG = Cipher.ALG_AES_BLOCK_128_CBC_NOPAD;
	private final byte ASSYMMETRIC_ALG = Cipher.ALG_RSA_PKCS1_OAEP;


	private final static byte STORE_KEY = (byte)0x20; 
	private final static byte GET_KEY = (byte)0x21; 
	private final static byte DIGEST_SHA256 = (byte)0x60;

	private final static byte GENERATE_KEYPAIR = (byte)0x30;
	private final static byte CHECK_KEYPAIR_INITIALIZED = (byte)0x31;
	private final static byte GET_PUBLIC_KEY_MODULUS = (byte)0x32;
	private final static byte GET_PUBLIC_KEY_EXPONENT = (byte)0x33;

	private final static byte GENERATE_SESSION_KEY = (byte)0x40;
	private final static byte CHECK_SESSION_KEY_INITIALIZED = (byte)0x41;

	private final static byte ASSYMMETRIC_ENCRYPTION = (byte)0x50;
	private final static byte SYMMETRIC_ENCRYPTION = (byte)0x51;
	private final static byte ASSYMMETRIC_DECRYPTION = (byte)0x52;
	private final static byte SYMMETRIC_DECRYPTION = (byte)0x53;


	short counter = 0x30;
	byte[] secret =  new byte[1024];
	boolean secretinitialized = false;
	short secretLength = 0;

	public static void install(byte[] aArray, short	sOffset, byte bLength){
		new ForAp(aArray, sOffset, bLength);
	}

	private ForAp(byte[] aArray, short sOffset, byte bLength){
		register(aArray, (short) (sOffset + 1),	aArray[sOffset]);
		
		
		/*cipherRSA = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
        cipherDES = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD,
                false);
        randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
        smartCardKeys = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_2048);
        smartCardKeys.genKeyPair();*/
        //buildKeys();
		

		// allocate 256 bytes of RAM memory (available from beginning of session till end of session)
		s_abRAMBuffer = JCSystem.makeTransientByteArray(sRAMBufferSize, JCSystem.CLEAR_ON_RESET);

		// allocate RAM to hold the Bytes to be encrypted
		m_inBuffer = JCSystem.makeTransientByteArray(sInBufferSize, JCSystem.CLEAR_ON_RESET);        

		// allocate RAM to hold the calculated Cipher Key
		m_resultBuffer = JCSystem.makeTransientByteArray(sResultBufferSize, JCSystem.CLEAR_ON_RESET);

		abRandomAES_KEY_ARRAYInRAM = JCSystem.makeTransientByteArray((short)32, JCSystem.CLEAR_ON_RESET);

		/*// allocate bytes of EEPROM memory
		s_abNVMBufferElementUseIndicator = new byte[NUMBER_OF_ELEMENTS_IN_TABLE];
		s_abNVMBufferHashBytes = new byte[NUMBER_OF_ELEMENTS_IN_TABLE*NUMBER_OF_BYTES_FOR_HASH_IN_TABLE];
		s_abNVMBufferPasswordBytes = new byte[NUMBER_OF_ELEMENTS_IN_TABLE*NUMBER_OF_BYTES_FOR_PASSWORD_IN_TABLE];
		s_abNVMCipherKeyBytes = new byte[NUMBER_OF_ELEMENTS_IN_TABLE*NUMBER_OF_BYTES_FOR_CIPHER_KEY_IN_TABLE];*/

		rnd = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM);

		// DES key instance ...
		aesSessionKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); // 256 bit

		// MyCipher instance
		// myCipher = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);

	}
	//implementation for select() and deselect()
	//from MultiSelectable interface
	public boolean select(boolean appInstAlreadyActive){
		return true;
	}
	public void deselect(boolean appInstStillActive){
		return;
	}

	public void process(APDU apdu) throws ISOException {
		if (selectingApplet())
			return;
		byte[] buffer = apdu.getBuffer();
		if ( ((byte)(buffer[ISO7816.OFFSET_CLA] & (byte)0xFC)) != (byte)0x90 )	{
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		}
		switch (buffer[ISO7816.OFFSET_INS]) {
		case (byte) 0x10:
			buffer[0] = (byte) 'H';
		buffer[1] = (byte) 'e';
		buffer[2] = (byte) 'l';
		buffer[3] = (byte) 'l';
		buffer[4] = (byte) 'o';
		buffer[5] = (byte) ',';
		buffer[6] = (byte) ' ';
		buffer[7] = (byte) 'S';
		buffer[8] = (byte) 'm';
		buffer[9] = (byte) 'a';
		buffer[10] = (byte) 'r';
		buffer[11] = (byte) 't';
		buffer[12] = (byte) 'c';
		buffer[13] = (byte) 'a';
		buffer[14] = (byte) 'r';
		buffer[15] = (byte) 'd';
		buffer[16] = (byte) ':';
		buffer[17] = (byte) ' ';
		// increase counter by one and return counter value
		buffer[18] = (byte) (counter++ % 10 + 0x30);
		apdu.setOutgoingAndSend((short) 0, (short) 19);
		break;
		case STORE_KEY:
			storeKey(apdu);
			break;
		case GET_KEY:
			getKey(apdu);
			break;
		case DIGEST_SHA256:
			digestSha256(apdu);
			break;
		case GENERATE_KEYPAIR:
			generateKeyPair(apdu);
			break;
		case CHECK_KEYPAIR_INITIALIZED:
			keyPairIsInitialized(apdu);
			break;
		case GET_PUBLIC_KEY_MODULUS :
			getPublicKeyModulus(apdu);
			break;
		case GET_PUBLIC_KEY_EXPONENT :
			getPublicKeyExponent(apdu);
			break;
		case GENERATE_SESSION_KEY :
			generateSessionKey(apdu);
			break;
		case CHECK_SESSION_KEY_INITIALIZED :
			if (sessionKeyIsInitialized()){
				buffer[0] = (byte) 't';
			}else{
				buffer[0] = (byte) 'f';
			}
			apdu.setOutgoingAndSend((short) 0, (short)1);
			break;
		case ASSYMMETRIC_ENCRYPTION:
			rsaEncryption(apdu);
			break;
		case SYMMETRIC_ENCRYPTION:
			aesEncryption(apdu);
			break;
		case ASSYMMETRIC_DECRYPTION:
			rsaDecryption(apdu);
			break;
		case SYMMETRIC_DECRYPTION:
			aesDecryption(apdu);
			break;
		default:
			ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
		}
	}	

	/**********************************************
	 * Real Code
	 **********************************************/
	// number of bytes in the command VerifyGlobal
	final static byte VERIFY_GLOBAL_APDU_DATA_LENGTH = 4; // global PIN is 4 bytes long

	/**
	 * variable-specific defines
	 */    
	// RAM buffer
	final static short sRAMBufferSize = (short) 256;

	final static short sInBufferSize = (short)(32+1);
	final static short sResultBufferSize = (short)(32+1);    

	// NVM buffer
	final static short sNVMBufferSize = (short) 256;

	/**
	 * global variables
	 */
	// necessary to be an "array" for clear on reset
	private static short[] sVerifyGlobalPinChecked; // only has one element for verifyChecked true or false
	private static short sVerifyGlobalPinRetryCounter; // 16 bits in NVM which holds the retry counter

	private static byte[] s_abRAMBuffer;
	private static byte[] m_inBuffer;
	private static byte[] m_resultBuffer;    

	// the following are arrays that -in conjunction with each other- have to be seen as a "table"
	// the first "column" is the use indicator which keeps track of the state of a row (used or unused)
	// the 2., 3., 4. "columns" are following
	private static byte[] s_abNVMBufferElementUseIndicator; // holds 0x00 if unused or 0xFF if used
	private static byte[] s_abNVMBufferHashBytes; // holds hash bytes
	private static byte[] s_abNVMBufferPasswordBytes; // holds password bytes
	private static byte[] s_abNVMCipherKeyBytes; // holds cipher key bytes

	// hard-coded to '1' '2' '3' '4'
	private static byte[] globalPin = { (byte)0x31, (byte)0x32, (byte)0x33, (byte)0x34};

	/**
	 * general definitions
	 */
	private static final byte VALUE_SUCCESS = (byte)0;
	private static final byte VALUE_FAIL = (byte)0xFF;

	private static final byte IS_EQUAL = (byte)0;
	private static final byte IS_GREATER_THAN = (byte)(1);
	private static final byte IS_LESS_THAN = (byte)(-1);

	private static final byte ELEMENT_UNUSED = (byte)0;
	private static final byte ELEMENT_USED = (byte)0xFF;

	private static final short NUMBER_OF_ELEMENTS_IN_TABLE = (short)256;


	// Major-version byte for "getversion"
	final static byte bVERSION_MAJOR = (byte) 0x01;
	// Minor-version byte for "getversion"
	final static byte bVERSION_MINOR = (byte) 0x00; //next: 01

	/*
    // "NORMAL"
    private static byte receiptTypeName_Normal[] = {'N','O','R','M','A','L'};
	 */

	RandomData rnd;
	private final byte[] abRandomAES_KEY_ARRAYInRAM;    

	private AESKey aesSessionKey;
	private byte aesAlgorithm = Cipher.ALG_AES_BLOCK_128_ECB_NOPAD;
	private short aesKeyLength = 32;//bytes KeyBuilder.LENGTH_AES_256;

	private Cipher myCipher;
	
	public static final byte AES_256 = 9;     // Reserved for AES with 256-bit key


	
	
	
	public void encryptSessionPacket(){
		//create new session key
		byte success = createNewSessionKey();
		if(success == VALUE_SUCCESS){
			byte[] sessionInfo = createSessionInfo();
		}			
	}
	
	/**
	 * function responsible to generate session key
	 */
	public byte createNewSessionKey(){
		// first of all create 32 random bytes and store them in RAM
		rnd.generateData(abRandomAES_KEY_ARRAYInRAM, (short)0, aesKeyLength);
		// use these random bytes as the key for AES
		aesSessionKey.setKey(abRandomAES_KEY_ARRAYInRAM,(short)0);

		return VALUE_SUCCESS;
	}

	private byte[] createSessionInfo(){
		byte[] keyBytes = new byte[aesKeyLength]; 
		byte length = aesSessionKey.getKey(keyBytes, (short)0);
		byte[] sessionInfo = new byte[keyBytes.length + 3];
		sessionInfo[0] = AES_256;
		Util.arrayCopy(keyBytes, (short)0, sessionInfo, (short)1, (short)keyBytes.length);
		addCheckSum(sessionInfo);
		return sessionInfo;
	}

	private void addCheckSum(byte[] sessionInfo){
		short check = 0;	        
		for (short i = 1; i != sessionInfo.length - 2; i++){
			check += sessionInfo[i] & 0xff;
		}	        
		sessionInfo[sessionInfo.length - 2] = (byte)(check >> 8);
		sessionInfo[sessionInfo.length - 1] = (byte)(check);
	}

	/**
     * Method to set the exponent and modulus of a rsa public key
     *
     * @param key rsa public key which component should be set
     */
    /*private void setPublicKey(RSAPublicKey key) {
        try {
            // set the exponent of the key
            key.setExponent(fileBuffer, (short) 0, (short) 3);
 
            // strip of any integer padding
            short off = 3;
            short length = fileSize;
            if (fileBuffer[off] == 0x00) {
                off++;
                length--;
            }
 
            // set the modulus of the key
            key.setModulus(fileBuffer, off, length);
        } catch (CryptoException ex) {
            ISOException.throwIt((short) (SW_CRYPTO_EXCEPTION
                    + ex.getReason()));
        }
    }*/



	/**********************************************
	 * End Real Code
	 **********************************************/
	/**
	 * Public/Private Key Pair
	 */
	RSAPublicKey publicKey;
	RSAPrivateKey privateKey;
	private void generateKeyPair(APDU apdu){
		byte[] outbuffer= new byte[]{(byte)'T' ,(byte)'r' ,(byte)'u' ,(byte)'e'} ;
		short outLength;
		try{
			KeyPair kp = new KeyPair(KEY_PAIR_ALG,KEY_PAIR_LENGTH);
			kp.genKeyPair();
			publicKey = (RSAPublicKey) kp.getPublic();
			privateKey = (RSAPrivateKey) kp.getPrivate();
		}catch(CryptoException e){
			if (e.getReason() == CryptoException.NO_SUCH_ALGORITHM)
				outbuffer = new byte[]{(byte)'A' ,(byte)'L' ,(byte)'G' ,(byte)'G'} ; 
			if (e.getReason() == CryptoException.ILLEGAL_VALUE)
				outbuffer = new byte[]{(byte)'V' ,(byte)'A' ,(byte)'L' ,(byte)'G'} ; 
			if(e.getReason() == CryptoException.UNINITIALIZED_KEY)
				outbuffer = new byte[]{(byte)'K' ,(byte)'E' ,(byte)'Y' ,(byte)'U'} ;
			if(e.getReason() == CryptoException.ILLEGAL_USE)
				outbuffer = new byte[]{(byte)'U' ,(byte)'s' ,(byte)'e' ,(byte)'U'} ;
			if(e.getReason() == CryptoException.INVALID_INIT)
				outbuffer = new byte[]{(byte)'i' ,(byte)'n' ,(byte)'i' ,(byte)'t'} ;

		}
		outLength = (short)outbuffer.length;
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
	}

	private void keyPairIsInitialized(APDU apdu){
		byte[] buffer = apdu.getBuffer();
		if (publicKey.isInitialized() && privateKey.isInitialized()){
			buffer[0] = (byte) 't';
		}else{
			buffer[0] = (byte) 'f';
		}
		apdu.setOutgoingAndSend((short) 0, (short) buffer.length);
	}

	private void getPublicKeyModulus(APDU apdu){
		byte[] outbuffer = new byte[publicKey.getSize()];
		short outLength = publicKey.getModulus(outbuffer,(short) 0);
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
	}

	private void getPublicKeyExponent(APDU apdu){
		byte[] outbuffer = new byte[publicKey.getSize()];
		short outLength = publicKey.getExponent(outbuffer,(short) 0);
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
	}


	/**
	 * Session key
	 */	
	private AESKey sessionKey;
	//boolean sessionKeyInitialized = false;
	private void generateSessionKey(APDU apdu){
		AESKey aeskey;
		byte[] outbuffer= new byte[]{(byte)'T' ,(byte)'r' ,(byte)'u' ,(byte)'e'} ;
		short outLength;
		try{
			aeskey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false);
			//sessionKey = newKey;
			if(aeskey.isInitialized()){
				//sessionKeyInitialized = true;
				outbuffer = new byte[]{(byte)'T' ,(byte)'r' ,(byte)'u' ,(byte)'e'} ;
			}else{
				//sessionKeyInitialized = false;
				outbuffer = new byte[]{(byte)'F' ,(byte)'a' ,(byte)'l' ,(byte)'s' ,(byte)'e' } ;
			}
		}catch(CryptoException e){
			if (e.getReason() == CryptoException.NO_SUCH_ALGORITHM){
				try{
					aeskey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_192, false);
				}catch(CryptoException ee){
					if (e.getReason() == CryptoException.NO_SUCH_ALGORITHM){
						try{
							aeskey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
						}catch(CryptoException eee){
							outbuffer = new byte[]{(byte)'F' ,(byte)'u' ,(byte)'c' ,(byte)'k'} ; 
						}
					}
				}

			}

			if (e.getReason() == CryptoException.ILLEGAL_VALUE)
				outbuffer = new byte[]{(byte)'V' ,(byte)'A' ,(byte)'L' ,(byte)'G'} ; 
			if(e.getReason() == CryptoException.UNINITIALIZED_KEY)
				outbuffer = new byte[]{(byte)'K' ,(byte)'E' ,(byte)'Y' ,(byte)'U'} ;
			if(e.getReason() == CryptoException.ILLEGAL_USE)
				outbuffer = new byte[]{(byte)'U' ,(byte)'s' ,(byte)'e' ,(byte)'U'} ;
			if(e.getReason() == CryptoException.INVALID_INIT)
				outbuffer = new byte[]{(byte)'i' ,(byte)'n' ,(byte)'i' ,(byte)'t'} ;

		}
		outLength = (short)outbuffer.length;
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
	}

	private boolean sessionKeyIsInitialized(){
		return true;
	}

	/**
	 * Assymmetric Algorithm
	 */
	RSAPublicKey publicKeyReciever;

	private void initializePublicKeyReciever(byte[] exponent,byte[] modulus ){
		publicKeyReciever = (RSAPublicKey) KeyBuilder.buildKey (KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_1024, false);
		publicKeyReciever.setExponent(exponent, (short) 0, (short) exponent.length);
		publicKeyReciever.setModulus(modulus, (short) 0, (short) modulus.length);
	}


	private void rsaEncryption(APDU apdu){
		byte[] apduBuffer = apdu.getBuffer();
		short byteRead = apdu.setIncomingAndReceive();
		byte[] pubKeyBlock = new byte[byteRead];
		Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, pubKeyBlock, (short) 0, byteRead);
		byte[] exponent = {(byte) 7};
		byte[] modulus = {(byte) 33};
		initializePublicKeyReciever(exponent, modulus);

		Cipher encryptCipher = Cipher.getInstance(ASSYMMETRIC_ALG, false);

		//FOR DEBUG//encryptCipher.init(publicKeyReciever, Cipher.MODE_ENCRYPT);
		encryptCipher.init(publicKey, Cipher.MODE_ENCRYPT);
		byte[] sessionKeyBytes = new byte[(short)(sessionKey.getSize()/8)];//bits to bytes
		byte sessionKeyLength = ((AESKey) sessionKey).getKey(sessionKeyBytes,(short) 0);
		short cyphertextLength = encryptCipher.doFinal(sessionKeyBytes, (short) 0, sessionKeyLength, apduBuffer, (short) 0);

		// Send results
		apdu.setOutgoing();
		apdu.setOutgoingLength((short) cyphertextLength);
		apdu.sendBytesLong(apduBuffer, (short) 0, (short) cyphertextLength);
	}

	private void rsaDecryption(APDU apdu){
		byte[] apduBuffer = apdu.getBuffer();
		short byteRead = apdu.setIncomingAndReceive();
		byte[] pubKeyBlock = new byte[byteRead];
		Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, pubKeyBlock, (short) 0, byteRead);
		byte[] exponent = {(byte) 7};
		byte[] modulus = {(byte) 33};
		initializePublicKeyReciever(exponent, modulus);

		Cipher encryptCipher = Cipher.getInstance(ASSYMMETRIC_ALG, false);

		encryptCipher.init(privateKey, Cipher.MODE_DECRYPT);
		byte[] sessionKeyBytes = new byte[(short)(sessionKey.getSize()/8)];//bits to bytes
		byte sessionKeyLength = ((AESKey) sessionKey).getKey(sessionKeyBytes,(short) 0);
		short cyphertextLength = encryptCipher.doFinal(sessionKeyBytes, (short) 0, sessionKeyLength, apduBuffer, (short) 0);

		// Send results
		apdu.setOutgoing();
		apdu.setOutgoingLength((short) cyphertextLength);
		apdu.sendBytesLong(apduBuffer, (short) 0, (short) cyphertextLength);
	}


	/**
	 * Symmetric Algorithm
	 */
	private void aesEncryption(APDU apdu){		
		byte[] apduBuffer = apdu.getBuffer();
		short dataLen = apdu.setIncomingAndReceive();
		byte[] dataBlock = new byte[dataLen];
		Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, dataBlock, (short) 0, dataLen);

		Cipher encryptCipher = Cipher.getInstance(SYMMETRIC_ALG, false);

		encryptCipher.init(sessionKey, Cipher.MODE_ENCRYPT);
		encryptCipher.doFinal(dataBlock, (short) 0, dataLen, apduBuffer, (short) 0);

		apdu.setOutgoing();
		apdu.setOutgoingLength(dataLen);
		apdu.sendBytesLong(apduBuffer, (short) 0, dataLen);
	}

	private void aesDecryption(APDU apdu){		
		byte[] apduBuffer = apdu.getBuffer();
		short dataLen = apdu.setIncomingAndReceive();
		byte[] dataBlock = new byte[dataLen];
		Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, dataBlock, (short) 0, dataLen);

		Cipher encryptCipher = Cipher.getInstance(SYMMETRIC_ALG, false);

		encryptCipher.init(sessionKey, Cipher.MODE_DECRYPT);
		encryptCipher.doFinal(dataBlock, (short) 0, dataLen, apduBuffer, (short) 0);

		apdu.setOutgoing();
		apdu.setOutgoingLength(dataLen);
		apdu.sendBytesLong(apduBuffer, (short) 0, dataLen);
	}



	/**
	 * Debug stuff 
	 */

	private void storeKey(APDU apdu) {
		byte[] apduBuffer = apdu.getBuffer();
		short dataLen = apdu.setIncomingAndReceive();
		secretLength = Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, secret, (short) 0, dataLen);
		secretinitialized = true;
	}


	private void getKey(APDU apdu) {
		byte[] outbuffer;
		short outLength;
		if(secretinitialized){
			outbuffer = secret;
			outLength = secretLength;

		} else {
			outbuffer = new byte[]{(byte)'N' ,(byte)'o' ,(byte)' ' ,(byte)'S' ,(byte)'e' ,(byte)'c' ,(byte)'r' ,(byte)'e', (byte)'t' } ;
			outLength = (short)outbuffer.length;
		}
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
	}

	private void digestSha256(APDU apdu) {
		byte[] outbuffer;
		short outLength;
		try{
			MessageDigest dig = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
			byte[] apduBuffer = apdu.getBuffer();
			short dataLen = apdu.setIncomingAndReceive();
			outbuffer = new byte[dig.getLength()];
			outLength = dig.doFinal(apduBuffer, ISO7816.OFFSET_CDATA, dataLen, outbuffer, (short) 0);
		}catch(CryptoException e){
			if (e.getReason() == CryptoException.NO_SUCH_ALGORITHM){
				outbuffer = new byte[]{(byte)'N' ,(byte)'o' ,(byte)' ' ,(byte)'A' ,(byte)'l' ,(byte)'g' ,(byte)'o' ,(byte)'r', (byte)'i' } ;
				outLength = (short)outbuffer.length;
			}
			outbuffer = new byte[]{(byte)'N' ,(byte)'o' ,(byte)' ' ,(byte)'S' ,(byte)'e' ,(byte)'c' ,(byte)'r' ,(byte)'e', (byte)'t' } ;
			outLength = (short)outbuffer.length;
		}		

		//outbuffer = apduBuffer;
		//outLength = dataLen;
		// COPY ENCRYPTED DATA INTO OUTGOING BUFFER */
		//byte[] outbuffer = new byte[]{0x01,0x02,0x03,0x04};
		//short outLength= (short) outbuffer.length;
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
		//Util.arrayCopyNonAtomic(outbuffer, (short) 0, apduBuffer, ISO7816.OFFSET_CDATA, dataLen); 
		//apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, dataLen);
	}

}
